/* * Jajuk * Copyright (C) The Jajuk Team * http://jajuk.info * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.jajuk.ui.views; import java.text.DateFormat; import java.text.NumberFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import javax.swing.SwingUtilities; import net.miginfocom.swing.MigLayout; import org.jajuk.base.Device; import org.jajuk.base.DeviceManager; import org.jajuk.base.File; import org.jajuk.base.FileManager; import org.jajuk.base.Genre; import org.jajuk.base.GenreManager; import org.jajuk.base.Track; import org.jajuk.base.TrackManager; import org.jajuk.events.JajukEvent; import org.jajuk.events.JajukEvents; import org.jajuk.events.ObservationManager; import org.jajuk.util.Conf; import org.jajuk.util.Messages; import org.jajuk.util.ReadOnlyIterator; import org.jajuk.util.UtilGUI; import org.jajuk.util.UtilString; import org.jajuk.util.log.Log; import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartPanel; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.CategoryAxis3D; import org.jfree.chart.axis.CategoryLabelPosition; import org.jfree.chart.axis.CategoryLabelPositions; import org.jfree.chart.axis.CategoryLabelWidthType; import org.jfree.chart.axis.NumberAxis3D; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.labels.StandardCategoryToolTipGenerator; import org.jfree.chart.labels.StandardPieSectionLabelGenerator; import org.jfree.chart.labels.StandardPieToolTipGenerator; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PiePlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.renderer.category.BarRenderer3D; import org.jfree.chart.urls.StandardCategoryURLGenerator; import org.jfree.data.category.CategoryDataset; import org.jfree.data.general.DatasetUtilities; import org.jfree.data.general.DefaultPieDataset; import org.jfree.text.TextBlockAnchor; import org.jfree.ui.RectangleAnchor; import org.jfree.ui.TextAnchor; import org.jfree.util.SortOrder; /** * Statistics view * <p> * Help perspective. */ public class StatView extends ViewAdapter { /** Generated serialVersionUID. */ private static final long serialVersionUID = 1L; /* * (non-Javadoc) * * @see org.jajuk.ui.IView#display() */ @Override public void initUI() { setLayout(new MigLayout("ins 0,gapx 4,gapy 5", "[grow][grow]", "[grow][grow]")); ObservationManager.register(this); update(new JajukEvent(JajukEvents.DEVICE_REFRESH)); } /* (non-Javadoc) * @see org.jajuk.events.Observer#getRegistrationKeys() */ @Override public Set<JajukEvents> getRegistrationKeys() { Set<JajukEvents> eventSubjectSet = new HashSet<JajukEvents>(); eventSubjectSet.add(JajukEvents.DEVICE_DELETE); eventSubjectSet.add(JajukEvents.DEVICE_REFRESH); return eventSubjectSet; } /** * Genre repartition pie. * * @return the chart */ private ChartPanel createGenreRepartition() { try { DefaultPieDataset pdata = null; JFreeChart jfchart = null; // data pdata = new DefaultPieDataset(); int iTotal = TrackManager.getInstance().getElementCount(); double dOthers = 0; // Prepare a map genre -> nb tracks Map<Genre, Integer> genreNbTracks = new HashMap<Genre, Integer>(GenreManager.getInstance() .getElementCount()); ReadOnlyIterator<Track> it = TrackManager.getInstance().getTracksIterator(); while (it.hasNext()) { Track track = it.next(); Genre genre = track.getGenre(); Integer nbTracks = genreNbTracks.get(genre); if (nbTracks == null) { genreNbTracks.put(genre, 1); } else { genreNbTracks.put(genre, nbTracks + 1); } } // Cleanup genre with weight < 5 % for (Map.Entry<Genre, Integer> entry : genreNbTracks.entrySet()) { double d = entry.getValue(); if (iTotal > 0 && d / iTotal < Conf.getFloat(CONF_STATS_MIN_VALUE_GENRE_DISPLAY) / 100) { // less than 5% -> go to others dOthers += d; } else { double dValue = Math.round(100 * (d / iTotal)); pdata.setValue(entry.getKey().getName2(), dValue); } } if (iTotal > 0 && dOthers > 0) { double dValue = Math.round(100 * (dOthers / iTotal)); pdata.setValue(Messages.getString("StatView.0"), dValue); } // chart jfchart = ChartFactory.createPieChart3D(Messages.getString("StatView.1"), pdata, true, true, true); // set the background color for the chart... PiePlot plot = (PiePlot) jfchart.getPlot(); plot.setLabelFont(PiePlot.DEFAULT_LABEL_FONT); plot.setNoDataMessage(Messages.getString("StatView.2")); plot.setForegroundAlpha(0.5f); plot.setBackgroundAlpha(0.5f); plot.setLabelGenerator(new StandardPieSectionLabelGenerator("{0} = {2}")); plot.setToolTipGenerator(new StandardPieToolTipGenerator("{0} = {2}")); return new ChartPanel(jfchart); } catch (RuntimeException e) { Log.error(e); return null; } } /** * Device size pie. * * @return the chart */ private ChartPanel createDeviceRepartition() { try { DefaultPieDataset pdata = null; JFreeChart jfchart = null; // data pdata = new DefaultPieDataset(); // prepare devices long lTotalSize = 0; double dOthers = 0; List<Device> devices = DeviceManager.getInstance().getDevices(); long[] lSizes = new long[DeviceManager.getInstance().getElementCount()]; ReadOnlyIterator<File> it = FileManager.getInstance().getFilesIterator(); while (it.hasNext()) { File file = it.next(); lTotalSize += file.getSize(); lSizes[devices.indexOf(file.getDirectory().getDevice())] += file.getSize(); } for (Device device : devices) { long lSize = lSizes[devices.indexOf(device)]; if (lTotalSize > 0 && (double) lSize / lTotalSize < 0.05) { // less than 5% -> go to others dOthers += lSize; } else { double dValue = Math.round((double) lSize / 1073741824); pdata.setValue(device.getName(), dValue); } } if (dOthers > 0) { double dValue = Math.round((dOthers / 1073741824)); pdata.setValue(Messages.getString("StatView.3"), dValue); } // chart jfchart = ChartFactory.createPieChart3D(Messages.getString("StatView.4"), pdata, true, true, true); // set the background color for the chart... PiePlot plot = (PiePlot) jfchart.getPlot(); plot.setLabelFont(PiePlot.DEFAULT_LABEL_FONT); plot.setNoDataMessage(Messages.getString("StatView.5")); plot.setForegroundAlpha(0.5f); plot.setBackgroundAlpha(0.5f); plot.setLabelGenerator(new StandardPieSectionLabelGenerator("{0} = {1} GB ({2})")); plot.setToolTipGenerator(new StandardPieToolTipGenerator("{0} = {1} GB ({2})")); return new ChartPanel(jfchart); } catch (RuntimeException e) { Log.error(e); return null; } } /** * Collection size bars. * * @return the chart */ private ChartPanel createCollectionSize() { try { final DateFormat additionFormatter = UtilString.getAdditionDateFormatter(); CategoryDataset cdata = null; JFreeChart jfchart = null; int iMonthsNumber = 5; // number of mounts we show, mounts // before are set together in 'before' long lSizeByMonth[] = new long[iMonthsNumber + 1]; // contains size ( in Go ) for each month, first cell is before // data int[] iMonths = getMonths(iMonthsNumber); ReadOnlyIterator<Track> tracks = TrackManager.getInstance().getTracksIterator(); while (tracks.hasNext()) { Track track = tracks.next(); int i = Integer.parseInt(additionFormatter.format(track.getDiscoveryDate())) / 100; for (int j = 0; j < iMonthsNumber + 1; j++) { if (i <= iMonths[j]) { lSizeByMonth[j] += track.getTotalSize(); } } } double[][] data = new double[1][iMonthsNumber + 1]; for (int i = 0; i < iMonthsNumber + 1; i++) { data[0][i] = (double) lSizeByMonth[i] / 1073741824; } cdata = DatasetUtilities.createCategoryDataset(new String[] { "" }, getMonthsLabels(iMonthsNumber), data); // chart, use local copy of method to use better format string for // tooltips jfchart = createBarChart3D(Messages.getString("StatView.7"), // chart // title Messages.getString("StatView.8"), // domain axis label Messages.getString("StatView.9"), // range axis label cdata, // data PlotOrientation.VERTICAL, // orientation false, // include legend true, // tooltips false, // urls "{1} = {2} GB"); CategoryPlot plot = jfchart.getCategoryPlot(); CategoryAxis axis = plot.getDomainAxis(); new CategoryLabelPosition(RectangleAnchor.TOP, TextBlockAnchor.TOP_RIGHT, TextAnchor.TOP_RIGHT, -Math.PI / 8.0, CategoryLabelWidthType.CATEGORY, 0); axis.setCategoryLabelPositions(CategoryLabelPositions.STANDARD); // set the background color for the chart... plot.setNoDataMessage(Messages.getString("StatView.10")); plot.setForegroundAlpha(0.5f); plot.setBackgroundAlpha(0.5f); // plot.setBackgroundImage(IconLoader.IMAGES_STAT_PAPER).getImage()); return new ChartPanel(jfchart); } catch (Exception e) { Log.error(e); return null; } } // copied method from ChartFactory to overwrite format of tooltips which is // otherwise hardcoded in ChartFactory /** * Creates the bar chart3 d. * * * @param title * @param categoryAxisLabel * @param valueAxisLabel * @param dataset * @param orientation * @param legend * @param tooltips * @param urls * @param format * * @return the j free chart */ public static JFreeChart createBarChart3D(String title, String categoryAxisLabel, String valueAxisLabel, CategoryDataset dataset, PlotOrientation orientation, boolean legend, boolean tooltips, boolean urls, String format) { if (orientation == null) throw new IllegalArgumentException("Null 'orientation' argument."); CategoryAxis categoryAxis = new CategoryAxis3D(categoryAxisLabel); ValueAxis valueAxis = new NumberAxis3D(valueAxisLabel); BarRenderer3D renderer = new BarRenderer3D(); if (tooltips) renderer.setBaseToolTipGenerator(new StandardCategoryToolTipGenerator(format, NumberFormat .getInstance())); if (urls) renderer.setBaseItemURLGenerator(new StandardCategoryURLGenerator()); CategoryPlot plot = new CategoryPlot(dataset, categoryAxis, valueAxis, renderer); plot.setOrientation(orientation); if (orientation == PlotOrientation.HORIZONTAL) { plot.setRowRenderingOrder(SortOrder.DESCENDING); plot.setColumnRenderingOrder(SortOrder.DESCENDING); } plot.setForegroundAlpha(0.75F); JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); ChartFactory.getChartTheme().apply(chart); return chart; } /** * Track number bars. * * @return the chart */ private ChartPanel createTrackNumber() { try { final DateFormat additionFormatter = UtilString.getAdditionDateFormatter(); CategoryDataset cdata = null; JFreeChart jfchart = null; // number of months we show, mounts // before are set together in 'before' int iMonthsNumber = 5; // contains number of tracks for each month, first cell is 'before' // data int iTracksByMonth[] = new int[iMonthsNumber + 1]; int[] iMounts = getMonths(iMonthsNumber); ReadOnlyIterator<Track> tracks = TrackManager.getInstance().getTracksIterator(); while (tracks.hasNext()) { Track track = tracks.next(); int i = Integer.parseInt(additionFormatter.format(track.getDiscoveryDate())) / 100; for (int j = 0; j < iMonthsNumber + 1; j++) { if (i <= iMounts[j]) { iTracksByMonth[j]++; } } } double[][] data = new double[1][iMonthsNumber + 1]; // cannot use System.arraycopy() here because we have different types in // the arrays... // System.arraycopy(iTracksByMonth, 0, data[0], 0, iMonthsNumber); for (int i = 0; i < iMonthsNumber + 1; i++) { data[0][i] = iTracksByMonth[i]; } cdata = DatasetUtilities.createCategoryDataset(new String[] { "" }, getMonthsLabels(iMonthsNumber), data); // chart, use local copy of method to use better format string for // tooltips jfchart = createBarChart3D(Messages.getString("StatView.12"), // chart // title Messages.getString("StatView.13"), // domain axis label Messages.getString("StatView.14"), // range axis label cdata, // data PlotOrientation.VERTICAL, // orientation false, // include legend true, // tooltips false, // urls "{1} = {2}"); CategoryPlot plot = jfchart.getCategoryPlot(); CategoryAxis axis = plot.getDomainAxis(); new CategoryLabelPosition(RectangleAnchor.TOP, TextBlockAnchor.TOP_RIGHT, TextAnchor.TOP_RIGHT, -Math.PI / 8.0, CategoryLabelWidthType.CATEGORY, 0); axis.setCategoryLabelPositions(CategoryLabelPositions.STANDARD); // set the background color for the chart... plot.setNoDataMessage(Messages.getString("StatView.15")); plot.setForegroundAlpha(0.5f); plot.setBackgroundAlpha(0.5f); // plot.setBackgroundImage(IconLoader.IMAGES_STAT_PAPER).getImage()); return new ChartPanel(jfchart); } catch (Exception e) { Log.error(e); return null; } } /* * (non-Javadoc) * * @see org.jajuk.ui.IView#getDesc() */ @Override public String getDesc() { return Messages.getString("StatView.16"); } /* * (non-Javadoc) * * @see org.jajuk.ui.Observer#update(java.lang.String) */ @Override public void update(JajukEvent event) { JajukEvents subject = event.getSubject(); if (JajukEvents.DEVICE_REFRESH.equals(subject) || JajukEvents.DEVICE_DELETE.equals(subject)) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { try { UtilGUI.waiting(); if (getComponentCount() > 0) { removeAll(); } ChartPanel cp1 = createGenreRepartition(); if (cp1 != null) { add(cp1); } ChartPanel cp2 = createCollectionSize(); if (cp2 != null) { add(cp2, "wrap"); } ChartPanel cp3 = createTrackNumber(); if (cp3 != null) { add(cp3); } ChartPanel cp4 = createDeviceRepartition(); if (cp4 != null) { add(cp4, "wrap"); } revalidate(); repaint(); } finally { UtilGUI.stopWaiting(); } } }); } } /** * Computes mounts labels. * * @param iMonthsNumber : number of mounts ( without 'before' ) you want * * @return the mounts labels */ private String[] getMonthsLabels(int iMonthsNumber) { int iNow = Integer.parseInt(new SimpleDateFormat(DATE_FILE, Locale.getDefault()) .format(new Date())) / 100; // reference // month String sMonths[] = new String[iMonthsNumber + 1]; // contains number of tracks for each month, first cell is 'before' int iYear = iNow / 100; int iMonth = Integer.parseInt(Integer.toString(iNow).substring(4, 6)); for (int k = 0; k < iMonthsNumber; k++) { sMonths[iMonthsNumber - k] = new StringBuilder().append((iMonth / 10 == 0) ? "0" : "") .append(Integer.toString(iMonth)).append('/').append(Integer.toString(iYear)).toString(); iMonth--; if (iMonth == 0) { iMonth = 12; iYear--; } } sMonths[0] = Messages.getString("StatView.24"); return sMonths; } /** * Get months as integers. * * @param iMonthsNumber * * @return the months */ private int[] getMonths(int iMonthsNumber) { int[] iMonths = new int[iMonthsNumber + 1]; String[] sMonths = getMonthsLabels(iMonthsNumber + 1); for (int i = 0; i < iMonthsNumber + 1; i++) { iMonths[i] = Integer .parseInt(sMonths[i + 1].substring(3, 7) + sMonths[i + 1].substring(0, 2)); } return iMonths; } }